package cn.virens.oauth;

import cn.virens.common.exception.APIException;
import cn.virens.common.spring.bean.SpringBeanProxy;
import cn.virens.common.spring.filter.AbstractPathMatcherFilter;
import cn.virens.oauth.exception.NoLoginException;
import cn.virens.oauth.request.VirensHttpServletRequest;
import cn.virens.oauth.request.principal.DelegatePrincipal;
import cn.virens.oauth.service.AuthenticationService;
import cn.virens.oauth.subject.Subject;
import cn.virens.oauth.subject.SubjectSimple;
import cn.virens.oauth.subject.SubjectUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;

/**
 * 登录验证拦截器,如果不符合条件将会根据对应的请求类型进行处理
 *
 * @author virens
 */
public class SimpleAuthenticationFilter extends AbstractPathMatcherFilter implements ApplicationContextAware {
    private AuthenticationService mAuthenticationService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filter) throws ServletException, IOException {
        VirensHttpServletRequest arequset = mAuthenticationService.authorization(request).map(principal -> {
            return new VirensHttpServletRequest(request, SubjectUtils.bind(newSubject(principal)));
        }).orElseThrow(() -> new NoLoginException("尚未登录,请登录后操作"));

        filter.doFilter(arequset, response);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.mAuthenticationService = SpringBeanProxy.proxy(AuthenticationService.class, applicationContext);
    }

    /**
     * 检查登录令牌的有效时间是否足够，如果不足需要对缓存进行刷新
     */
    protected DelegatePrincipal refresh(DelegatePrincipal principal) throws APIException {
        if (principal != null && LocalDateTime.now().isAfter(principal.getRetryTime())) {
            principal.setRetryTime(LocalDateTime.now().plusDays(getExpire() / 2));
            principal.setExpireTime(LocalDateTime.now().plusDays(getExpire()));

            mAuthenticationService.refresh(principal);
        }

        return principal;
    }

    /**
     * 根据登录令牌新建主题(用于绑定线程)
     */
    protected Subject newSubject(DelegatePrincipal principal) throws APIException {
        return new SubjectSimple(mAuthenticationService, refresh(principal));
    }

    /**
     * 获取缓存过期时间
     */
    protected Integer getExpire() throws APIException {
        return mAuthenticationService.getExpire();
    }

}
